////////////////////////////////////////////////////////////////////////////////
// Filename: modelclass.cpp
////////////////////////////////////////////////////////////////////////////////
#include "modelclass.h"
#include "CUtilities.h"


ModelClass::ModelClass()
{
	m_effect = 0;
	m_technique = 0;
	m_layout = 0;

	m_worldMatrixPtr = 0;
	m_viewMatrixPtr = 0;
	m_projectionMatrixPtr = 0;

	m_vertexBuffer = 0;
	m_indexBuffer = 0;

#if NewStuff

	m_ParticleBuffer = 0;

#endif

	m_texturePtr = 0;
	m_texture = 0;

#if NewStuff

	m_ParticleCount = 100;
	Particles = 0;

#endif

	RNGen::GetInstance();


}


ModelClass::ModelClass(const ModelClass& other)
{
}


ModelClass::~ModelClass()
{
}


bool ModelClass::Initialize(ID3D10Device* device, HWND hwnd)
{
	bool result;


	// Initialize the shader that will be used to draw the triangle.
	result = InitializeShader(device, hwnd);
	if(!result)
	{
		return false;
	}

	// Initialize the vertex and index buffer that hold the geometry for the triangle.
	result = InitializeBuffers(device);
	if(!result)
	{
		return false;
	}

	// Initialize the texture that will be rendered on the triangle.
	result = InitializeTexture(device, hwnd);
	if(!result)
	{
		return false;
	}

	return true;
}


void ModelClass::Shutdown()
{
	// Release the texture.
	ShutdownTexture();

	// Release the vertex and index buffers.
	ShutdownBuffers();

	// Shutdown the shader effect.
	ShutdownShader();

	return;
}


void ModelClass::Render(ID3D10Device* device)
{
	// Put the vertex and index buffers on the graphics pipeline to prepare them for drawing.
	RenderBuffers(device);

	// Now render the prepared buffers with the shader.
	RenderShader(device);

	return;
}


bool ModelClass::InitializeShader(ID3D10Device* device, HWND hwnd)
{
	HRESULT result;
	ID3D10Blob* errorMessage;
	char* compileErrors;
	unsigned long bufferSize, i;
	ofstream fout;
	D3D10_INPUT_ELEMENT_DESC polygonLayout[2];
	unsigned int numElements;
    D3D10_PASS_DESC passDesc;


	// Initialize the error message.
	errorMessage = 0;

	// Load the shader in from the file.
	result = D3DX10CreateEffectFromFile(L"shader002.fx", NULL, NULL, "fx_4_0", D3D10_SHADER_ENABLE_STRICTNESS, 0, 
										device, NULL, NULL, &m_effect, &errorMessage, NULL);
	if(FAILED(result))
	{
		// If the shader failed to compile it should have writen something to the error message.
		if(errorMessage)
		{
			// Get a pointer to the error message text buffer.
			compileErrors = (char*)(errorMessage->GetBufferPointer());

			// Get the length of the message.
			bufferSize = errorMessage->GetBufferSize();

			// Open a file to write the error message to.
			fout.open("shader-error.txt");

			// Write out the error message.
			for(i=0; i<bufferSize; i++)
			{
				fout << compileErrors[i];
			}

			// Close the file.
			fout.close();

			// Release the error message.
			errorMessage->Release();
			errorMessage = 0;

			// Pop a message up on the screen to notify the user to check the text file for compile errors.
			MessageBox(hwnd, L"Error compiling shader.  Check shader-error.txt for message.", L"Shader Error", MB_OK);
		}
		else
		{
			MessageBox(hwnd, L"Could not find file: ../Engine/shader002.fx", L"Shader Error", MB_OK);
		}

		return false;
	}

	// Get a pointer to the technique inside the shader.
	m_technique = m_effect->GetTechniqueByName("TextureTechnique");
	if(!m_technique)
	{
		return false;
	}

	// Now setup the layout of the data that goes into the shader.
	// This setup needs to match the vertex stucture in this class and in the shader.
	polygonLayout[0].SemanticName = "POSITION";
	polygonLayout[0].SemanticIndex = 0;
	polygonLayout[0].Format = DXGI_FORMAT_R32G32B32_FLOAT;
	polygonLayout[0].InputSlot = 0;
	polygonLayout[0].AlignedByteOffset = 0;
	polygonLayout[0].InputSlotClass = D3D10_INPUT_PER_VERTEX_DATA;
	polygonLayout[0].InstanceDataStepRate = 0;

	polygonLayout[1].SemanticName = "TEXCOORD";
	polygonLayout[1].SemanticIndex = 0;
	polygonLayout[1].Format = DXGI_FORMAT_R32G32_FLOAT;
	polygonLayout[1].InputSlot = 0;
	polygonLayout[1].AlignedByteOffset = D3D10_APPEND_ALIGNED_ELEMENT;
	polygonLayout[1].InputSlotClass = D3D10_INPUT_PER_VERTEX_DATA;
	polygonLayout[1].InstanceDataStepRate = 0;

	// Get a count of the elements in the layout.
    numElements = sizeof(polygonLayout) / sizeof(polygonLayout[0]);

	// Get the description of the first pass described in the shader technique.
    m_technique->GetPassByIndex(0)->GetDesc(&passDesc);

	// Create the input layout.
    result = device->CreateInputLayout(polygonLayout, numElements, passDesc.pIAInputSignature, passDesc.IAInputSignatureSize, 
									   &m_layout);
	if(FAILED(result))
	{
		return false;
	}

	// Get pointers to the three matrices inside the shader so we can update them from this class.
    m_worldMatrixPtr = m_effect->GetVariableByName("worldMatrix")->AsMatrix();
	m_viewMatrixPtr = m_effect->GetVariableByName("viewMatrix")->AsMatrix();
    m_projectionMatrixPtr = m_effect->GetVariableByName("projectionMatrix")->AsMatrix();

	// Get pointer to the texture resource inside the shader.
	m_texturePtr = m_effect->GetVariableByName("shaderTexture")->AsShaderResource();

	return true;
}


void ModelClass::ShutdownShader()
{
	// Release the pointer to the texture in the shader file.
	m_texturePtr = 0;

	// Release the pointers to the matrices inside the shader.
	m_worldMatrixPtr = 0;
	m_viewMatrixPtr = 0;
	m_projectionMatrixPtr = 0;

	// Release the pointer to the shader layout.
	if(m_layout)
	{
		m_layout->Release();
		m_layout = 0;
	}

	// Release the pointer to the shader technique.
	m_technique = 0;

	// Release the pointer to the shader.
	if(m_effect)
	{
		m_effect->Release();
		m_effect = 0;
	}

	return;
}

#if NewStuff

bool ModelClass::FillParticles()
{
	for(int i = 0; i < this->m_ParticleCount; i++)
	{
		Particles[i].position = D3DXVECTOR3(1.0f, 1.0f, 1.0f);
		Particles[i].color = D3DXCOLOR(1.0f, 0.0f, 0.0f, 1.0f);
	}

	return true;
}

#endif


bool ModelClass::InitializeBuffers(ID3D10Device* device)
{
	VertexType* vertices;

	D3D10_BUFFER_DESC vertexBufferDesc;
    D3D10_SUBRESOURCE_DATA vertexData;

#if NewStuff

	D3D10_BUFFER_DESC ParticleBufferDesc;
	D3D10_SUBRESOURCE_DATA ParticleData;

#endif

	unsigned long* indices;
	D3D10_BUFFER_DESC indexBufferDesc;
    D3D10_SUBRESOURCE_DATA indexData;
	HRESULT result;

#if !Grid
	// Set the number of vertices in the vertex array.
	m_vertexCount = 4000;

	// Create the vertex array.
	vertices = new VertexType[m_vertexCount];
	if(!vertices)
	{
		return false;
	}
#endif

#if NewStuff

	Particles = new ParticleType[m_ParticleCount];
	if(!Particles)
		return false;

#endif

	

#if Grid

	float limit = 50;

	unsigned int LineCount;

	LineCount = (limit * 4)+2;

	m_vertexCount = LineCount*2;

	// Create the vertex array.
	vertices = new VertexType[m_vertexCount];
	if(!vertices)
	{
		return false;
	}

	unsigned int counter = 0;

	unsigned int i;

	for(i = 0; i < ((m_vertexCount-4)>>1); i+=2)
	{
		vertices[i].position = D3DXVECTOR3(-limit, 0, -limit + counter);
		vertices[i].texture = D3DXVECTOR2(0.0f, 1.0f);

		vertices[i+1].position = D3DXVECTOR3(limit, 0, -limit + counter);
		vertices[i+1].texture = D3DXVECTOR2(0.0f, 1.0f);

		counter++;
	}

	counter = 0;

	for(i; i < (m_vertexCount-4); i+=2)
	{
		vertices[i].position = D3DXVECTOR3(-limit + counter, 0, -limit);
		vertices[i].texture = D3DXVECTOR2(0.0f, 1.0f);

		vertices[i+1].position = D3DXVECTOR3(-limit + counter, 0, limit);
		vertices[i+1].texture = D3DXVECTOR2(0.0f, 1.0f);

		counter++;
	}

	vertices[m_vertexCount-1].position = D3DXVECTOR3(-limit, 0, limit);
	vertices[m_vertexCount-1].texture = D3DXVECTOR2(0.0f, 1.0f);

	vertices[m_vertexCount-2].position = D3DXVECTOR3(limit, 0, limit);
	vertices[m_vertexCount-2].texture = D3DXVECTOR2(0.0f, 1.0f);

	vertices[m_vertexCount-3].position = D3DXVECTOR3(limit, 0, limit);
	vertices[m_vertexCount-3].texture = D3DXVECTOR2(0.0f, 1.0f);

	vertices[m_vertexCount-4].position = D3DXVECTOR3(limit,0, -limit);
	vertices[m_vertexCount-4].texture = D3DXVECTOR2(0.0f, 1.0f);

#endif


#if !Grid

	float limit = 20.0f;

	for(unsigned int i = 0; i < m_vertexCount; i++)
	{
		// Load the vertex array with data.
		vertices[i].position = D3DXVECTOR3(RNGen::GetInstance()->RandF(FASTSEED, -limit, limit), RNGen::GetInstance()->RandF(FASTSEED, -limit, limit), RNGen::GetInstance()->RandF(FASTSEED, -limit, limit));  // Bottom left.
		vertices[i].texture = D3DXVECTOR2(0.0f, 1.0f);
	}
#endif

	

#if NewStuff

	this->FillParticles();

#endif

	// Set the number of indices in the index array.
	m_indexCount = m_vertexCount;

	// Create the index array.
	indices = new unsigned long[m_indexCount];
	if(!indices)
	{
		return false;
	}

	for(unsigned int j = 0; j < m_indexCount; j++)
	{
		indices[j] = j;
	}

	// Set up the description of the vertex buffer.
    vertexBufferDesc.Usage = D3D10_USAGE_DEFAULT;
    vertexBufferDesc.ByteWidth = sizeof(VertexType) * m_vertexCount;
    vertexBufferDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
    vertexBufferDesc.CPUAccessFlags = 0;
    vertexBufferDesc.MiscFlags = 0;

#if NewStuff

	ParticleBufferDesc.ByteWidth = sizeof(ParticleType) * m_ParticleCount;
	ParticleBufferDesc.Usage = D3D10_USAGE_DYNAMIC;
	ParticleBufferDesc.BindFlags = D3D10_BIND_VERTEX_BUFFER;
	ParticleBufferDesc.CPUAccessFlags = D3D10_CPU_ACCESS_WRITE;
	ParticleBufferDesc.MiscFlags = 0;

#endif

	// Give the subresource structure a pointer to the vertex data.
    vertexData.pSysMem = vertices;

#if NewStuff

	ParticleData.pSysMem = Particles;

#endif

	// Now finally create the vertex buffer.
    result = device->CreateBuffer(&vertexBufferDesc, &vertexData, &m_vertexBuffer);
	if(FAILED(result))
	{
		return false;
	}

#if NewStuff

	result = device->CreateBuffer(&ParticleBufferDesc, &ParticleData, &m_ParticleBuffer);
	if(FAILED(result))
	{
		return false;
	}

#endif

	// Set up the description of the index buffer.
    indexBufferDesc.Usage = D3D10_USAGE_DEFAULT;
    indexBufferDesc.ByteWidth = sizeof(unsigned long) * m_indexCount;
    indexBufferDesc.BindFlags = D3D10_BIND_INDEX_BUFFER;
    indexBufferDesc.CPUAccessFlags = 0;
    indexBufferDesc.MiscFlags = 0;

	// Give the subresource structure a pointer to the index data.
    indexData.pSysMem = indices;

	// Create the index buffer.
	result = device->CreateBuffer(&indexBufferDesc, &indexData, &m_indexBuffer);
	if(FAILED(result))
	{
		return false;
	}

	// Release the arrays now that the vertex and index buffers have been created and loaded.
	delete [] vertices;
	vertices = 0;

	delete [] indices;
	indices = 0;

#if NewStuff

	delete [] this->Particles;
	Particles = 0;

#endif

	return true;
}


void ModelClass::ShutdownBuffers()
{
	// Release the index buffer.
	if(m_indexBuffer)
	{
		m_indexBuffer->Release();
		m_indexBuffer = 0;
	}

	// Release the vertex buffer.
	if(m_vertexBuffer)
	{
		m_vertexBuffer->Release();
		m_vertexBuffer = 0;
	}

	return;
}


void ModelClass::RenderBuffers(ID3D10Device* device)
{
	unsigned int stride;
	unsigned int offset;

#if NewStuff

	unsigned int Stride2;

#endif


	// Set vertex buffer stride and offset.
    stride = sizeof(VertexType);
	offset = 0;

#if NewStuff

	Stride2 = sizeof(ParticleType);

#endif 
    
	// Set the vertex buffer to active in the input assembler so it can be rendered.
	device->IASetVertexBuffers(0, 1, &m_vertexBuffer, &stride, &offset);

    // Set the index buffer to active in the input assembler so it can be rendered.
    device->IASetIndexBuffer(m_indexBuffer, DXGI_FORMAT_R32_UINT, 0);

    // Set the type of primitive that should be rendered from this vertex buffer, in this case triangles.
#if Grid
    device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_LINELIST);
#endif

#if !Grid
	device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);

#endif

#if NewStuff

	// Set the vertex buffer to active in the input assembler so it can be rendered.
	device->IASetVertexBuffers(0, 1, &m_ParticleBuffer, &Stride2, &offset);

	device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);

#endif

	return;
}


void ModelClass::RenderShader(ID3D10Device* device)
{
    D3D10_TECHNIQUE_DESC techniqueDesc;
	unsigned int i;
	

	// Set the input layout.
	device->IASetInputLayout(m_layout);

	// Bind the texture.
	m_texturePtr->SetResource(m_texture);

	// Get the description structure of the technique from inside the shader so it can be used for rendering.
    m_technique->GetDesc(&techniqueDesc);

    // Go through each pass in the technique (should be just one currently) and render the triangles.
	for(i=0; i<techniqueDesc.Passes; ++i)
    {
        m_technique->GetPassByIndex(i)->Apply(0);
        device->DrawIndexed(m_indexCount, 0, 0);
    }

	return;
}


void ModelClass::SetMatrices(D3DXMATRIX worldMatrix, D3DXMATRIX viewMatrix, D3DXMATRIX projectionMatrix)
{
	// Set the world matrix variable inside the shader.
    m_worldMatrixPtr->SetMatrix((float*)&worldMatrix);

	// Set the view matrix variable inside the shader.
	m_viewMatrixPtr->SetMatrix((float*)&viewMatrix);

	// Set the projection matrix variable inside the shader.
    m_projectionMatrixPtr->SetMatrix((float*)&projectionMatrix);

	return;
}


bool ModelClass::InitializeTexture(ID3D10Device* device, HWND hwnd)
{
	HRESULT result;


	// Load the texture in.
	result = D3DX10CreateShaderResourceViewFromFile(device, L"data/seafloor.dds", NULL, NULL, &m_texture, NULL);
	if(FAILED(result))
	{
		MessageBox(hwnd, L"Error loading the seafloor.dds texture.", L"Texture Error", MB_OK);
		return false;
	}

	return true;
}


void ModelClass::ShutdownTexture()
{
	// Release the texture resource.
	if(m_texture)
	{
		m_texture->Release();
		m_texture = 0;
	}

	return;
}